/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.feign.decoder;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.iwindplus.base.domain.enums.BizCodeEnum;
import com.iwindplus.base.domain.exception.BizException;
import com.iwindplus.base.domain.vo.ResultVO;
import com.iwindplus.base.util.JacksonUtil;
import com.iwindplus.base.feign.domain.property.FeignProperty;
import feign.Request;
import feign.Response;
import feign.RetryableException;
import feign.codec.ErrorDecoder;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;

/**
 * Feign 统一异常解码器 —— Feign 11.8+ 优化版
 *
 * @author zengdegui
 * @since 2025/09/28
 */
@Slf4j
public class FeignErrorDecoder extends ErrorDecoder.Default {

    private final FeignProperty property;

    public FeignErrorDecoder(FeignProperty property) {
        this.property = property;
    }

    @Override
    public Exception decode(String methodKey, Response response) {
        // 1. 先交给默认解码器，保留原始异常信息
        Exception exception = super.decode(methodKey, response);

        // 2. 如果关闭自定义解码，直接返回
        if (!property.getError().getEnabled()) {
            return exception;
        }

        int status = response.status();
        log.warn("Feign请求失败, 方法={}, 状态码={}", methodKey, status);

        // 3. 重试异常直接透传
        if (exception instanceof RetryableException) {
            return exception;
        }

        // 4. 503 直接抛 BizException
        if (status == HttpStatus.SERVICE_UNAVAILABLE.value()) {
            return new BizException(HttpStatus.SERVICE_UNAVAILABLE);
        }

        // 5. 500-511 且 GET 请求，开启重试
        if (isRetryApplicable(status, response)) {
            return retry(response, exception);
        }

        // 6. 其它情况解析响应体
        ResultVO<Object> result = parseResponse(response);
        return new BizException(
            result.getBizStatus(),
            result.getBizCode(),
            result.getBizMessage(),
            result.getBizMessageParams());
    }

    private Exception retry(Response response, Exception exception) {
        Long retryAfter = null;
        return new RetryableException(response.status(), exception.getMessage(), response.request().httpMethod(), exception, retryAfter,
            response.request());
    }

    /* ======================== 辅助方法 ======================== */

    private boolean isRetryApplicable(int status, Response response) {
        return status > HttpStatus.INTERNAL_SERVER_ERROR.value()
            && status <= HttpStatus.NETWORK_AUTHENTICATION_REQUIRED.value()
            && response.request().httpMethod() == Request.HttpMethod.GET
            && Boolean.TRUE.equals(property.getRetry().getEnabled());
    }

    private ResultVO<Object> parseResponse(Response response) {
        if (response.body() == null) {
            return ResultVO.error(BizCodeEnum.RPC_ERROR);
        }
        // Feign 自动管理流关闭
        try (Reader reader = response.body().asReader(Charset.defaultCharset())) {
            String bodyStr = IoUtil.read(reader);
            if (ObjectUtil.isEmpty(bodyStr)) {
                return ResultVO.error(BizCodeEnum.RPC_ERROR);
            }
            return JacksonUtil.parseObject(bodyStr, new TypeReference<>() {
            });
        } catch (IOException e) {
            log.error("读取Feign响应体失败", e);
            return ResultVO.error(BizCodeEnum.RPC_ERROR);
        }
    }
}